package shira.sort;

import java.util.List;

import shira.exception.sort.SortingException;
import shira.person.Educative;
import shira.person.Ken;
import shira.person.Madrich;
import shira.seminar.Hug;

/**
 * A sorter. The heart of Shira. Sorts educatives into hugs.
 * 
 * @author avian
 */
public class Sorter {

	/* --- Public Methods --- */

	/**
	 * Sorts the given educatives into the given hugs using the given sorting
	 * score.
	 * 
	 * @param sortingScore
	 *            The sorting score to sort with.
	 * @param educatives
	 *            The educatives to sort.
	 * @param hugs
	 *            The hugs to sort into.
	 * @return The sorted hugs.
	 * @throws SortingException
	 */
	public List<Hug> sort(SortingScore sortingScore,
			List<Educative> educatives, List<Hug> hugs) throws SortingException {
		for (Educative educative : educatives) {
			if (educative.getHug() != null) {
				continue;
			}
			int minScoreDifference = Integer.MAX_VALUE;
			Hug bestHug = null;
			for (Hug hug : hugs) {
				if (madrichKenViolation(educative, hug)) {
					continue;
				}
				int beforeScore = hug.calculateScore(sortingScore);
				hug.addEducative(educative);
				int afterScore = hug.calculateScore(sortingScore);
				int scoreDifference = afterScore - beforeScore;
				if (scoreDifference < minScoreDifference) {
					minScoreDifference = scoreDifference;
					bestHug = hug;
				}
				hug.removeEducative(educative);
			}
			if (bestHug == null) {
				throw new SortingException("No best hug was found for educative: " + educative.getName());
			}
			bestHug.addEducative(educative);
		}
		return hugs;
	}

	/* --- Private Methods --- */

	/**
	 * Checks if the new educative violates the same-ken-as-madrich constraint.
	 * 
	 * @param educative
	 *            The new educative.
	 * @param hug
	 *            The hug.
	 * @return True if the new educative violates it, false otherwise.
	 */
	private boolean madrichKenViolation(Educative educative, Hug hug) {
		Ken educativeKen = educative.getKen();
		if (educativeKen == null) {
			// null is not a ken
			return false;
		}
		for (Madrich madrich : hug.getMadrichim()) {
			if (!madrich.isAllowSameKen() && madrich.getKen() == educativeKen) {
				return true;
			}
		}
		return false;
	}
}
